package com.example.gobang.game;

import com.example.gobang.constant.Constants;
import com.example.gobang.manager.OnlineUserManager;
import com.example.gobang.manager.RoomManger;
import com.example.gobang.model.MatchResponse;
import com.example.gobang.model.Result;
import com.example.gobang.model.Room;
import com.example.gobang.model.UserInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

@Component
@Slf4j
// 匹配器，通过这个类负责完成整个匹配功能
public class Matcher {
    // 创建 3 个队列
    private Queue<UserInfo> normalQueue = new LinkedList<>();
    private Queue<UserInfo> highQueue = new LinkedList<>();
    private Queue<UserInfo> veryHighQueue = new LinkedList<>();
    @Autowired
    private OnlineUserManager onlineUserManager;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private RoomManger roomManger;

    // 将玩家放入到匹配队列中
    public void add(UserInfo userInfo) {
        if (userInfo.getScore() < Constants.NORMAL_SCORE) {
            synchronized (normalQueue) {
                normalQueue.offer(userInfo);
                normalQueue.notify();
            }
            log.info("将玩家 " + userInfo.getUserName() + " 加入到了 normalQueue 中");
        } else if (userInfo.getScore() >= Constants.NORMAL_SCORE && userInfo.getScore() < Constants.HIGH_SCORE) {
            synchronized (highQueue) {
                highQueue.offer(userInfo);
                highQueue.notify();
            }
            log.info("将玩家 " + userInfo.getUserName() + " 加入到了 highQueue 中");
        }  else {
            synchronized (veryHighQueue) {
                veryHighQueue.offer(userInfo);
                veryHighQueue.notify();
            }
            log.info("将玩家 " + userInfo.getUserName() + " 加入到了 veryHighQueue 中");
        }
    }

    // 用户点击停止匹配，将玩家从匹配队列中删除
    public void remove(UserInfo userInfo) {
        if (userInfo.getScore() < Constants.NORMAL_SCORE) {
            synchronized (normalQueue) {
                normalQueue.remove(userInfo);
            }
            log.info("将玩家 " + userInfo.getUserName() + " 从 normalQueue 中移除");
        } else if(userInfo.getScore() >= Constants.NORMAL_SCORE && userInfo.getScore() < Constants.HIGH_SCORE) {
            synchronized (highQueue) {
                highQueue.remove(userInfo);
            }
            log.info("将玩家 " + userInfo.getUserName() + " 从 highQueue 中移除");
        }  else {
            synchronized (veryHighQueue) {
                veryHighQueue.remove(userInfo);
            }
            log.info("将玩家 " + userInfo.getUserName() + " 从 veryHighQueue 中移除");
        }
    }

    public Matcher() {
        // 创建 3 个线程，分别针对这3个匹配队列进行操作
        Thread normalThread = new Thread() {
            @Override
            public void run() {
                // 扫描 normalQueue
                while (true) {
                    handlerMatch(normalQueue);
                }
            }
        };
        normalThread.start();

        Thread highThread = new Thread() {
            @Override
            public void run() {
                while (true) {
                    handlerMatch(highQueue);
                }
            }
        };
        highThread.start();

        Thread veryHighThread = new Thread() {
            @Override
            public void run() {
                while (true) {
                    handlerMatch(veryHighQueue);
                }
            }
        };
        veryHighThread.start();
    }

    private void handlerMatch(Queue<UserInfo> matchQueue) {
        synchronized (matchQueue) {
            try {
                // 1. 检测队列中元素是否 > 2
                while (matchQueue.size() < 2) {
                    matchQueue.wait();
                }
                // 2. 尝试从队列中取出两个玩家
                UserInfo player1 = matchQueue.poll();
                UserInfo player2 = matchQueue.poll();
                log.info("handlerMatch 匹配到玩家1：{}，玩家2：{}", player1.getUserName(), player2.getUserName());
                // 3. 获取玩家会话
                WebSocketSession playerSession1 = onlineUserManager.getFromGameHall(player1.getUserId());
                WebSocketSession playerSession2 = onlineUserManager.getFromGameHall(player2.getUserId());
                // 匹配队列的玩家必须在线（前面逻辑进行了处理，当玩家断开连接时就将玩家从匹配队列中移除）
                // 但此处仍进行一次判定
                if(playerSession1 == null) {
                    // 玩家1不在线，将玩家2放回匹配队列中
                    matchQueue.offer(player2);
                    return;
                }
                if(playerSession2 == null) {
                    // 玩家2不在线，将玩家1放回匹配队列中
                    matchQueue.offer(player1);
                    return;
                }
                // 避免多开（防止前面逻辑出现BUG）
                if(playerSession1 == playerSession2) {
                    matchQueue.offer(player1);
                    return;
                }
                // 4. TODO 将这两个玩家放到同一个房间进行游戏
                Room room = new Room();
                roomManger.add(room, player1.getUserId(), player2.getUserId());
                // 5. 告诉玩家已成功匹配
                // 玩家1
                Result result1 = new Result();
                result1.setStatus(Constants.MATCH_SUCCESS);
                result1.setData("匹配成功");
                String json1 = objectMapper.writeValueAsString(result1);
                playerSession1.sendMessage(new TextMessage(json1));

                // 玩家2
                Result result2 = new Result();
                result2.setStatus(Constants.MATCH_SUCCESS);
                result2.setData("匹配成功");
                String json2 = objectMapper.writeValueAsString(result2);
                playerSession2.sendMessage(new TextMessage(json2));
                log.info("json1: " + json1 + "json2: " + json2);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
